﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using JXT.PrimaryKey.Batman.Reflection;
using JXT.PrimaryKey.Batman.Domain.Models;
using JXT.PrimaryKey.Batman.Domain.Specifications;
using JXT.PrimaryKey.Batman.Domain.Specifications.Expressions;
using JXT.PrimaryKey.Batman.Domain.Repositories.MySql.Expressions;

namespace JXT.PrimaryKey.Batman.Domain.Repositories.MySql
{
    public static class SpecificationExtensions
    {
        public static ExpressionSpecification<TEntity> ToExpressionSpecification<TEntity>(this ISpecification<TEntity> specification) where TEntity : IEntity
        {
            if (specification == null)
                return null;
            if (specification is ExpressionSpecification<TEntity>)
                return specification as ExpressionSpecification<TEntity>;
            else
                throw new ArgumentException("only alowed expression specification!");
        }

        public static OrderedExpressionSpecification<TEntity> ToExpressionSpecification<TEntity>(this IOrderedSpecification<TEntity> specification) where TEntity : IEntity
        {
            if (specification == null)
                return null;
            if (specification is OrderedExpressionSpecification<TEntity>)
                return specification as OrderedExpressionSpecification<TEntity>;
            else
                throw new ArgumentException("only alowed expression specification!");
        }

        public static PagedExpressionSpecification<TEntity> ToExpressionSpecification<TEntity>(this IPagedSpecification<TEntity> specification) where TEntity : IEntity
        {
            if (specification == null)
                return null;
            if (specification is PagedExpressionSpecification<TEntity>)
                return specification as PagedExpressionSpecification<TEntity>;
            else
                throw new ArgumentException("only alowed expression specification!");
        }

        public static string BuildQueryString<TEntity>(this ISpecification<TEntity> specification, out IDataParameter[] parameters) where TEntity : IEntity
        {
            var expressionSpecification = specification.ToExpressionSpecification();
            string tableName = typeof(TEntity).Name.ToLower();
            string sql = "select * from `{0}`";
            sql = String.Format(sql, tableName);
            parameters = null;
            if (expressionSpecification != null && expressionSpecification.WhereExpression != null)
            {
                WhereQuery<TEntity> query = new WhereQuery<TEntity>();
                query.And(expressionSpecification.WhereExpression);
                sql += " where " + query.QueryText;
                parameters = query.Parameters;
            }
            return sql;
        }

        public static string BuildSingleQueryString<TEntity>(this ISpecification<TEntity> specification, out IDataParameter[] parameters) where TEntity : IEntity
        {
            var expressionSpecification = specification.ToExpressionSpecification();
            string tableName = typeof(TEntity).Name.ToLower();
            string sql = "select * from `{0}`";
            sql = String.Format(sql, tableName);
            parameters = null;
            if (expressionSpecification != null && expressionSpecification.WhereExpression != null)
            {
                WhereQuery<TEntity> query = new WhereQuery<TEntity>();
                query.And(expressionSpecification.WhereExpression);
                sql += " where " + query.QueryText;
                parameters = query.Parameters;
                var identityName = typeof(TEntity).GetEntityIdentityName();
                if (!String.IsNullOrEmpty(identityName))
                    sql += String.Format(" order by `{0}` desc limit 1", identityName);
            }
            return sql;
        }

        public static string BuildQueryString<TEntity>(this IOrderedSpecification<TEntity> specification, out IDataParameter[] parameters) where TEntity : IEntity
        {
            var expressionSpecification = specification.ToExpressionSpecification();
            string tableName = typeof(TEntity).Name.ToLower();
            string sql = "select * from `{0}`";
            sql = String.Format(sql, tableName);
            parameters = null;
            if (expressionSpecification != null && expressionSpecification.WhereExpression != null)
            {
                WhereQuery<TEntity> query = new WhereQuery<TEntity>();
                query.And(expressionSpecification.WhereExpression);
                sql += " where " + query.QueryText;
                parameters = query.Parameters;
            }
            if (expressionSpecification != null && expressionSpecification.OrderExpressionQueue.Count > 0)
            {
                OrderQuery<TEntity> orderQuery = new OrderQuery<TEntity>();
                foreach (var item in expressionSpecification.OrderExpressionQueue)
                {
                    if (item.Item2 == Specifications.OrderType.Ascending)
                    {
                        orderQuery.Ascending(item.Item1);
                    }
                    else
                    {
                        orderQuery.Descending(item.Item1);
                    }
                }
                sql += orderQuery.QueryText;
            }
            return sql;
        }

        public static string BuildQueryString<TEntity>(this IPagedSpecification<TEntity> specification, out IDataParameter[] parameters) where TEntity : IEntity
        {
            var expressionSpecification = specification.ToExpressionSpecification();
            string tableName = typeof(TEntity).Name.ToLower();
            string sql = "select * from `{0}`";
            sql = String.Format(sql, tableName);
            parameters = null;
            string whereString = "";
            if (expressionSpecification != null && expressionSpecification.WhereExpression != null)
            {
                WhereQuery<TEntity> query = new WhereQuery<TEntity>();
                query.And(expressionSpecification.WhereExpression);
                whereString += " where " + query.QueryText;
                parameters = query.Parameters;
            }
            string orderString = "";
            if (expressionSpecification != null && expressionSpecification.OrderExpressionQueue.Count > 0)
            {
                OrderQuery<TEntity> orderQuery = new OrderQuery<TEntity>();
                foreach (var item in expressionSpecification.OrderExpressionQueue)
                {
                    if (item.Item2 == Specifications.OrderType.Ascending)
                    {
                        orderQuery.Ascending(item.Item1);
                    }
                    else
                    {
                        orderQuery.Descending(item.Item1);
                    }
                }
                orderString = " " + orderQuery.QueryText;
            }
            if (expressionSpecification != null && specification.IsEnablePaged)
            {
                int skip = (specification.PageIndex - 1) * specification.PageSize;
                int take = specification.PageSize;
                sql = String.Format("select * from `{0}` {1}{2} limit {3},{4}", tableName, whereString, orderString, skip, take);
            }
            else
            {
                sql = String.Format(sql, tableName) + whereString + orderString;
            }
            return sql;
        }
    }
}
